package api

import (
	"awesomeProject/user-web/forms"
	"awesomeProject/user-web/global"
	"awesomeProject/user-web/global/response"
	"awesomeProject/user-web/middlewares"
	"awesomeProject/user-web/models"
	"awesomeProject/user-web/proto"
	"context"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"github.com/go-redis/redis/v8"
	"go.uber.org/zap"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"net/http"

	"strconv"
	"strings"
	"time"
)

// 全局变量
var (
// 注意用户服务的端口！！！我们写入了配置文件中
// 不能把端口设置为全局变量，因为还没有初始化
)

func removeTopStruct(fields map[string]string) map[string]string {

	rsp := map[string]string{}

	for filed, err := range fields {
		rsp[filed[strings.Index(filed, ".")+1:]] = err
	}
	return rsp
}

// HandleValidateError 进行表单验证的内部逻辑
func HandleValidateError(err error, c *gin.Context) {

	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		c.JSON(http.StatusOK, gin.H{"msg": err.Error()})
	}
	c.JSON(http.StatusBadRequest, gin.H{"error": removeTopStruct(errs.Translate(global.Trans))})
}

// HandleGrpcErrorToHttp 将Grpc的错误转化为HTTP的错误
func HandleGrpcErrorToHttp(err error, c *gin.Context) {
	if err == nil {
		return
	}
	// 错误不为空才进行转换
	e, ok := status.FromError(err)
	if ok {
		switch e.Code() {
		case codes.NotFound:
			c.JSON(http.StatusNotFound, gin.H{"msg": e.Message()})
		case codes.Internal:
			c.JSON(http.StatusInternalServerError, gin.H{"msg": "内部错误"})
		case codes.InvalidArgument:
			c.JSON(http.StatusBadRequest, gin.H{"msg": "参数错误"})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{"msg": "其他错误"})
		}
	}
}

func GetUserList(c *gin.Context) {

	claims, _ := c.Get("claims")
	curUser := claims.(*models.CustomClaims)

	zap.S().Infof("当前用户: id[%d], nickname[%v]", curUser.ID, curUser.NickName)

	// 通过url获取参数，并转化为int
	pn := c.DefaultQuery("pn", "1")
	pSize := c.DefaultQuery("psize", "10")
	pnInt, _ := strconv.Atoi(pn)
	pSizeInt, _ := strconv.Atoi(pSize)
	// 生成grpc的调用客户端

	rsp, err := global.UserSrvClient.GetUserList(context.Background(), &proto.PageInfoRequest{
		Pn:    uint32(pnInt),
		PSize: uint32(pSizeInt),
	})

	if err != nil {
		zap.S().Errorf("[GetUserList]调用接口失败: %v", err.Error())
		HandleGrpcErrorToHttp(err, c)
		return
	}

	result := make([]interface{}, 0)
	for _, val := range rsp.Data {
		//data := make(map[string]interface{})

		user := response.UserResponse{
			Id:       val.Id,
			NickName: val.NickName,
			Gender:   val.Gender,
			Birthday: response.JsonTime(time.Unix(int64(val.Birthday), 0)),
			Phone:    val.Phone,
		}

		result = append(result, user)
	}

	c.JSON(http.StatusOK, result) // 这个方法会自动序列化！！！
}

func PassWordLogin(c *gin.Context) {

	// 需要通过表单输入用户密码，也就是需要表单验证
	pwdLoginForm := forms.PassWordLoginForm{}
	if err := c.ShouldBind(&pwdLoginForm); err != nil {
		HandleValidateError(err, c)
		return
	}

	// 进行验证码验证
	ok := store.Verify(pwdLoginForm.CaptchaId, pwdLoginForm.Captcha, true) // 从表单中获取验证码信息，true表示验证后删除
	if !ok {
		c.JSON(http.StatusBadRequest, gin.H{
			"captcha": "验证码错误",
		})
		return
	}

	// 登录的逻辑
	// 1.查询有无手机号
	rsp, err := global.UserSrvClient.GerUserByPhone(context.Background(), &proto.PhoneRequest{
		Phone: pwdLoginForm.Phone,
	})
	//zap.S().Infof("通过GerUserByPhone获得用户信息为: %v", rsp)
	// 查不到这个手机号对应的用户
	if err != nil {
		zap.S().Info("当前错误: " + err.Error())
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusBadRequest, map[string]string{
					"msg": "用户不存在",
				})
			default:
				c.JSON(http.StatusInternalServerError, map[string]string{
					"msg": "登录失败: " + e.Message(),
				})
			}

			return
		}
	} else {
		// 查询到用户了，现在开始检测密码
		pwd_rsp, pwd_err := global.UserSrvClient.VerifyPassword(context.Background(), &proto.VerifyRequest{
			Password:          pwdLoginForm.Password,
			EncryptedPassword: rsp.Password, // 从数据库中读取的
		})
		if pwd_err != nil {
			c.JSON(http.StatusInternalServerError, map[string]string{
				"msg": "登录失败: " + pwd_err.Error(),
			})
		} else {
			// 没有出现错误，判断密码是否正确，正确就登陆成功
			if pwd_rsp.Success {
				// 生成token
				j := middlewares.NewJWT()
				// 进行签名
				zap.S().Infof("当前用户role为%d", rsp.Role)
				claims := models.CustomClaims{
					ID:          uint(rsp.Id),
					NickName:    rsp.NickName,
					AuthorityId: uint(rsp.Role),
					StandardClaims: jwt.StandardClaims{
						NotBefore: time.Now().Unix(),               // 签名的创建时间
						ExpiresAt: time.Now().Unix() + 60*60*24*30, // 签名的过期时间 --- 设置30天过期
						Issuer:    "sale-system",                   // 需要和kong中Create jwt模块中的key保持一致
					},
				}

				zap.S().Infof("当前claims.AuthorityId为%d", claims.AuthorityId)
				// 生成token
				token, err := j.CreateToken(claims)

				if err != nil {
					c.JSON(http.StatusInternalServerError, gin.H{
						"msg": "生成token失败",
					})
					return
				}
				c.JSON(http.StatusOK, gin.H{
					"id":         string(rsp.Id),
					"nick_name":  rsp.NickName,
					"token":      token,
					"expired_at": strconv.FormatInt((time.Now().Unix()+60*60*24*30)*1000, 10),
				})
			} else {
				c.JSON(http.StatusBadRequest, map[string]string{
					"msg": "密码错误",
				})
			}

		}
	}

}

// Register 用户注册
func Register(c *gin.Context) {

	// 获取表单
	registerForm := forms.RegisterForm{}
	// 进行验证
	if err := c.ShouldBind(&registerForm); err != nil {
		HandleValidateError(err, c)
		return
	}

	// 验证码校验 -- 通过redis客户端
	rdb := redis.NewClient(&redis.Options{
		Addr: fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
	})

	code, err := rdb.Get(context.Background(), registerForm.Phone).Result() // 我们设置的key是对应的电话，val是验证码
	if err == redis.Nil {                                                   // 表示redis中不存在这个key
		c.JSON(http.StatusBadRequest, gin.H{
			"code": "验证码不存在",
		})
		return
	}
	// 存在这个键值对
	if code != registerForm.Code {
		c.JSON(http.StatusBadRequest, gin.H{
			"code": "验证码错误",
		})
		return
	}
	// 是否需要删掉这个key? 已经完成验证了
	rdb.Del(context.Background(), registerForm.Phone)

	user, err := global.UserSrvClient.CreateUser(context.Background(), &proto.CreateUserRequest{
		NickName: registerForm.Phone,
		Phone:    registerForm.Phone,
		Password: registerForm.Password,
	})

	if err != nil {
		zap.S().Errorf("CreateUser() 调用失败: %s", err.Error())
		HandleValidateError(err, c)
		return
	}

	// 注册完成，一般默认实现登录，这时生成token
	j := middlewares.NewJWT()
	// 进行签名
	claims := models.CustomClaims{
		ID:          uint(user.Id),
		NickName:    user.NickName,
		AuthorityId: uint(user.Role),
		StandardClaims: jwt.StandardClaims{
			NotBefore: time.Now().Unix(),               // 签名的创建时间
			ExpiresAt: time.Now().Unix() + 60*60*24*30, // 签名的过期时间 --- 设置30天过期
			Issuer:    "sale-system",                   // 需要和kong中Create jwt模块中的key保持一致
		},
	}

	// 生成token
	token, err := j.CreateToken(claims)

	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": "生成token失败",
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"id":         string(user.Id),
		"nick_name":  user.NickName,
		"token":      token,
		"expired_at": strconv.FormatInt((time.Now().Unix()+60*60*24*30)*1000, 10),
	})
}

func GetUserById(c *gin.Context) {

}

func GetUserByPhone(c *gin.Context) {

}

func CreateUser(c *gin.Context) {

}

func UpdateUser(c *gin.Context) {

}
